use crate::{code::Code, runner::NodeList};

use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt, Default)]
#[structopt(name = "fst", about = "a light script parse and runner for work")]
pub struct Config {
    #[structopt(name = "FILE", parse(from_os_str))]
    pub file: PathBuf,

    /// provide running input args
    #[structopt(short, long)]
    pub args: Vec<String>,

    /// enable debug mode
    #[structopt(short = "d", long)]
    pub debug: bool,

    /// running command string code directly
    #[structopt(short = "c", long)]
    pub code: Option<String>,

    /// remove inline running tips
    #[structopt(short = "s", long)]
    pub silent: bool,
}

impl Config {
    pub fn may_exit(&self) {
        if self.debug {
            println!("");
        } else {
            std::process::exit(0);
        }
    }

    pub fn read_args() -> Self {
        Config::from_args()
    }
}

/// Fst 主程序
pub struct App {
    codes: Code,
    pub config: Config,
}

impl App {
    pub fn new<T, U>(code: T, config: U) -> Self
    where
        T: Into<Code>,
        U: Into<Config>,
    {
        Self {
            codes: code.into(),
            config: config.into(),
        }
    }

    pub fn from_split(code: String, pat: &str, config: Config) -> Self {
        let codes = Code::from(
            code.split(pat)
                .map(ToString::to_string)
                .collect::<Vec<String>>(),
        );
        Self { codes, config }
    }

    pub fn from_file<P: AsRef<std::path::Path>>(path: P, config: Config) -> Self {
        let codes = Code::from_file(&path).unwrap_or_else(|_| {
            crate::error!(
                "Path of file {} is not found 😱",
                crate::color!(cyan => path.as_ref().display()),
            );

            if path.as_ref().starts_with("http://") || path.as_ref().starts_with("https://") {
                crate::tip!("Http remote file address is not valid");
                crate::tip!("Fst may do this function in the future, but not now 😥");
            }

            crate::tip!("Try to check the path of the file");

            std::process::exit(0);
        });
        Self { codes, config }
    }

    pub fn nodes(&self) -> NodeList {
        self.codes.to_nodes()
    }

    pub fn auto_load() -> Self {
        let config = Config::read_args();
        let code = format!("{}", config.file.display());
        if let Some(ref pat) = config.code {
            App::from_split(code, &pat.clone(), config)
        } else {
            App::from_file(config.file.clone(), config)
        }
    }

    pub fn auto_run(&mut self) {
        let mut nodes = self.nodes();
        crate::work::init(&mut nodes);
        nodes.run(&mut self.config);
    }
}
